﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;
using BLToolkit.Data;
using Rsdn.Janus.CoreServices.Extensibility;
using Rsdn.Janus.Framework;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
    public class JanusApplicationContext : ApplicationContext
    {
        private static IntPtr hEvent = IntPtr.Zero;
        private Mutex mutex = null;
        private JanusHost host = null;
        Form janusMainForm = null;
        private bool initializationInProgress = false;
        ServiceManager rootManager = null;
        private IDisposable initScope = null;

        public JanusApplicationContext()
        {
            InitNonVisualPart();
        }

        /// <summary>
        /// Inits the non visual part.
        /// With small init time
        /// </summary>
        private void InitNonVisualPart()
        {
            TraceVerbose("Instance startup");
#if DEBUG
            DbManager.TraceSwitch = new TraceSwitch("DbManager", "DbManager trace switch", "Info");
#endif
            //string appPath = Path.GetDirectoryName(Application.ExecutablePath);

            initializationInProgress = true;
            SharedFunctions.RegisterApplication(Application.ExecutablePath);
            Application.ApplicationExit += Application_ApplicationExit;
            try
            {
                Console.WriteLine(
                    @"Janus project. Copyright (C) 2002-2011 by Rsdn Team. " +
                    @"See rsdn.ru for more information.");

                // Проверка на единственность экземпляра приложения.
                bool createdNew;
                mutex = new Mutex(true, SharedConstants.JanusRunningInstanceDetectionMutex, out createdNew);
                if (!mutex.WaitOne(0, false))
                {
                    WindowActivator.ActivateWindow(Rsdn.Janus.MainForm.GetCaption());
                    ExitThread();
                    return;
                }

                // Create the named event only
                hEvent = SharedEvents.CreateEvent(SharedConstants.JanusStartedEvent);


                //// Создаем контрол, чтобы инициализировать винформсный констекст синхронизации,
                //// если он еще не инициализирован
                //using (new Control())
                //{
                //}

                Thread.CurrentThread.CurrentUICulture = new CultureInfo((int) Config.Instance.UILanguage);

                rootManager = new ServiceManager(true);

                if (!CheckEnvironment())
                {
                    ExitThread();
                    return;
                }

                TraceVerbose("ResMgr");



                if (Config.Instance.ShowSplash)
                {
                    EventHandler hider = null;
                    IServiceRegistrationCookie informerCookie = null;
                    IServiceRegistrationCookie progressCookie = null;

                    hider =
                        (sender, e) =>
                            {
                                if (!initializationInProgress)
                                {
                                    // ReSharper disable AccessToModifiedClosure
                                    if (progressCookie != null)
                                    {
                                        rootManager.Unpublish(progressCookie);
                                    }

                                    rootManager.Publish<IProgressService>(new DefaultProgressService(rootManager));

                                    if (informerCookie != null)
                                    {
                                        rootManager.Unpublish(informerCookie);
                                    }

                                    SplashHelper.Hide();
                                    Application.Idle -= hider;
                                    // ReSharper restore AccessToModifiedClosure
                                }
                            };

                    Application.Idle += hider;

                    informerCookie = rootManager.Publish(SplashHelper.Show(rootManager));

                    progressCookie = rootManager.Publish(SplashHelper.GetProgressService());

                    //Thread.Sleep(20000);
                }
                else
                {
                    rootManager.Publish<IProgressService>(new DefaultProgressService(rootManager));
                }


                host = new JanusHost(rootManager);
                MainForm = new InvisibleInitializationForm(rootManager, host);

                initScope = host.InitScope();

                rootManager.SetSplashMessage(SR.Splash.ApplicationStart);

                TraceVerbose("JanusHost");

                ISharedInfoService sharedInfoService = host.GetRequiredService<ISharedInfoService>();
                Config.Instance.UpdateSharedInfo(sharedInfoService);

                // Подписка сервиса на извещения об изменении конфигурации.
                var configChangedNotifier = host.GetRequiredService<ConfigChangedNotifier>();
                Config.Instance.ConfigChanged +=
                    (o, args) => configChangedNotifier.FireConfigChanged(Config.Instance);

                Application.ThreadException += ApplicationException;

                rootManager.Publish<IMainWindowService>(
                    new MainWindowService(() => ApplicationManager.Instance.MainForm));
                rootManager.Publish(new DockManager(() => ApplicationManager.Instance.MainForm.DockPanel));

            }
            catch (Exception ex)
            {
                using (ExceptionForm exceptionForm = new ExceptionForm(null, ex, false))
                {
                    exceptionForm.ShowDialog();
                }
            }
        }

        private void ApplicationException(object sender, ThreadExceptionEventArgs e)
        {
            using (ExceptionForm exceptionForm = new ExceptionForm(host, e.Exception, false))
            {
                IUIShell uiShell = null;
                if (!initializationInProgress)
                {
                    uiShell = rootManager.GetRequiredService<IUIShell>();
                }
                IWin32Window mainWindowParent = null;
                if (uiShell != null)
                {
                    mainWindowParent = uiShell.GetMainWindowParent();
                }

                if (SplashHelper.GetSplashWindow() != null && mainWindowParent == null)
                {
                    mainWindowParent = SplashHelper.GetSplashWindow();
                }

                exceptionForm.ShowDialog(mainWindowParent);
            }
        }

        protected override void OnMainFormClosed(object sender, EventArgs e)
        {
            if (sender is InvisibleInitializationForm)
            {
                InvisibleInitializationForm initializationForm = sender as InvisibleInitializationForm;
                if (initializationForm.AbortApplication)
                {
                    base.OnMainFormClosed(sender, e);
                }
                else
                {
                    TraceVerbose("Ready to Run()");
                    // must be initialized before init scope is finished
                    // important to initialize in the same thread where MainForm created
                    Func<IWin32Window> parentWindowGetter = delegate { return ApplicationManager.Instance.MainForm; };
                    rootManager.Publish<IUIShell>(new UIShell(parentWindowGetter,OnUiFreezer));
                    if (initScope!=null)
                    {
                        initScope.Dispose();
                        initScope = null;
                    }

                    initializationInProgress = false;

                    ApplicationManager.Instance.Run(host);
                    janusMainForm = ApplicationManager.Instance.MainForm;

                    MainForm = janusMainForm;
                    janusMainForm.Show();
                }
            }
            else if (sender == janusMainForm)
            {
                base.OnMainFormClosed(sender, e);
            }
        }

        private void OnUiFreezer(bool freeze)
        {
            ApplicationManager.Instance.MainForm.Enabled = freeze;
        }

        void Application_ApplicationExit(object sender, EventArgs e)
        {
            if (mutex != null)
            {
                mutex.Close();
                mutex = null;
            }

            if (host != null)
            {
                host.Dispose();
                host = null;
            }
        }


        private static bool CheckEnvironment()
        {
            if (Environment.Version < new Version(2, 0, 50727, 1433))
            {
                using (SP1RequiredForm frm = new SP1RequiredForm())
                {
                    frm.ShowDialog();
                }
                return false;
            }

            return true;
        }

        #region Debug

        private static readonly Stopwatch _sw = Stopwatch.StartNew();

        [Conditional("DEBUG")]
        public static void TraceVerbose(string label)
        {
            Debug.WriteLineIf(TraceSwitch.TraceVerbose, _sw.Elapsed + ": " + label, TraceSwitch.DisplayName);
        }

        private static TraceSwitch _traceSwitch;

        private static TraceSwitch TraceSwitch
        {
            get { return _traceSwitch ?? (_traceSwitch = new TraceSwitch("EntryPoint", "Program entry point trace switch")); }
        }

        #endregion
    }
}